﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TraceMessage.cs" company="AzureContrib (Open source library http://azurecontrib.codeplex.com)">
//   Microsoft Public License (Ms-PL)
// </copyright>
// <summary>
//   A <see cref="Message" /> to trace to a given <see cref="Category" /> for a given <see cref="Switch" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Web.Script.Serialization;

using AzureContrib.WindowsAzure.Core;
using AzureContrib.WindowsAzure.Diagnostics.Properties;

namespace AzureContrib.WindowsAzure.Diagnostics
{
	/// <summary>
	/// A <see cref="Message"/> to trace to a given <see cref="Category"/> for a given <see cref="Switch"/>
	/// </summary>
	public class SerializableTraceMessage : TraceMessage
	{
		/// <summary>
		/// 64 * 1024 bytes  = 64KB
		/// </summary>
		public const int MaxMessageBuffer = 64 * 1024;

		private static JavaScriptSerializer javaScriptSerializer;

		/// <summary>
		/// Initializes a new instance of the<see cref="TraceMessage"/>class.
		/// </summary>
		public SerializableTraceMessage()
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="TraceMessage"/> class. 
		/// </summary>
		/// <param name="level">Trace level to trace at.</param>
		/// <param name="switch">Identifying switch.</param>
		/// <param name="message">The message traced.</param>
		public TraceMessage(TraceLevel level, string @switch, string message)
		{
			Category = level.ToString();
			Switch = @switch;
			Message = message;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="TraceMessage"/> class. 
		/// </summary>
		/// <param name="category">The category to trace to.</param>
		/// <param name="switch">Identifying switch.</param>
		/// <param name="message">The message traced.</param>
		public TraceMessage(string category, string @switch, string message)
		{
			Category = category;
			Switch = @switch;
			Message = message;
		}

		/// <summary>
		/// Gets or sets the <see cref="Category"/> for this trace message.
		/// </summary>
		public string Category { get; set; }

		/// <summary>
		/// Gets or sets the identifying switch we trace from.
		/// </summary>
		public string Switch { get; set; }

		/// <summary>
		/// Gets or sets the message traced.
		/// </summary>
		public string Message { get; set; }

		private static JavaScriptSerializer JavaScriptSerializer
		{
			get
			{
				return javaScriptSerializer ?? (javaScriptSerializer = new JavaScriptSerializer { MaxJsonLength = MaxMessageBuffer });
			}
		}

		/// <summary>
		/// Turn a <see cref="string"/> into a <see cref="TraceMessage"/>.
		/// </summary>
		/// <param name="traceMessage">The <see cref="TraceMessage"/> as a <see cref="string"/>.</param>
		/// <returns>A <see cref="TraceMessage"/> created from the incomming <see cref="string"/>.</returns>
		/// <exception cref="ArgumentException">If the <paramref name="traceMessage"/> is not a valid serialized <see cref="TraceMessage"/></exception>
		public static implicit operator TraceMessage(string traceMessage)
		{
			Assumes.NotNullOrEmpty(traceMessage, "traceMessage", Strings.NullOrEmptyTraceMessageString);
			return JavaScriptSerializer.Deserialize<TraceMessage>(traceMessage);
		}

		/// <summary>
		/// Is the <paramref name="message"/> a valid <see cref="string"/> represenation of a <see cref="TraceMessage"/>.
		/// </summary>
		/// <param name="message">The message as a <see cref="string"/>.</param>
		/// <returns>True if the message is validated.</returns>
		public static bool IsValidMessage(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				return false;
			}

			TraceMessage traceMessage;
			try
			{
				traceMessage = message;
			}
			catch (ArgumentNullException)
			{
				return false;
			}
			catch (ArgumentException)
			{
				return false;
			}
			catch (InvalidOperationException)
			{
				return false;
			}

			// traceMessage.TraceLevel must have been deserialized or exception.
			return !string.IsNullOrEmpty(traceMessage.Message) &&
						 !string.IsNullOrEmpty(traceMessage.Switch);
		}

		/// <summary>
		/// Try to parse out a <see cref="TraceMessage"/> fro the <paramref name="message"/>
		/// </summary>
		/// <param name="message">The <see cref="string"/> message to try to parse.</param>
		/// <param name="traceMessage">The resulting parsed <see cref="TraceMessage"/> or null.</param>
		/// <returns>True if the <paramref name="message"/> is a <see cref="TraceMessage"/>.</returns>
		public static bool TryParse(string message, out TraceMessage traceMessage)
		{
			traceMessage = null;

			if (IsValidMessage(message))
			{
				traceMessage = message;
			}

			return traceMessage != null;
		}

		/// <summary>
		/// Turn a <see cref="string"/> into a <see cref="TraceMessage"/> instance.
		/// </summary>
		/// <param name="traceMessage">The <see cref="TraceMessage"/> as a <see cref="string"/></param>
		/// <returns>A <see cref="TraceMessage"/> created from the <see cref="string"/>.</returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static TraceMessage FromString(string traceMessage)
		{
			Assumes.NotNullOrEmpty(traceMessage, "traceMessage", Strings.ExpectedARealTraceMessage);
			return traceMessage;
		}

		/// <summary>
		/// Turn a <see cref="TraceMessage"/> into a <see cref="string"/>.
		/// </summary>
		/// <param name="traceMessage">The <see cref="TraceMessage"/> to convert.</param>
		/// <returns>A <see cref="TraceMessage"/> created from the incomming <see cref="string"/>.</returns>
		[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "AzureContrib.WindowsAzure.Core.Assumes.NotNull handles null checking.")]
		public static implicit operator string(TraceMessage traceMessage)
		{
			Assumes.NotNull(traceMessage, "traceMessage");
			return traceMessage.ToString();
		}

		/// <summary>
		/// The <see cref="TraceMessage"/> as a <see cref="string"/>.
		/// </summary>
		/// <returns>The instance value as a <see cref="string"/>.</returns>
		public override string ToString()
		{
			return JavaScriptSerializer.Serialize(this);
		}
	}
}